#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <uv.h>

#define DEFAULT_PORT 7000
#define DEFAULT_HOST "127.0.0.1"

char received_data[1024];
int received_data_len = 0;

static void close_cb(uv_handle_t *handle) {
    (void)handle;
    fprintf(stdout, "Client close connection\n");
}

static void alloc_cb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
    (void)handle;
    buf->base = malloc(suggested_size);
    buf->len = suggested_size;
}

static void read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
    if (nread <= 0) {
        if (nread < 0 && nread != UV_EOF) {
            fprintf(stderr, "Client read error: %s\n", uv_strerror(nread));
        } else {
            fprintf(stdout, "Client EOF\n");
            uv_read_stop(stream);
        }
        uv_close((uv_handle_t *)stream, close_cb);
    } else if (nread > 0) {
        memcpy(received_data + received_data_len, buf->base, nread);
        received_data_len += nread;
        fprintf(stdout, "Client received: '%.*s'\n", (int)nread, buf->base);
        if (received_data_len >= 13) {
            fprintf(stdout, "Client finished\n");
            uv_close((uv_handle_t *)stream, close_cb);
        }
    }

    free(buf->base);
}

static void write_cb(uv_write_t *req, int status) {
    if (status < 0) {
        fprintf(stderr, "Client write error: %s\n", uv_strerror(status));
    } else {
        uv_read_start(req->handle, alloc_cb, read_cb);
    }
    free(req->bufs);
    free(req);
}

static void write_data(uv_stream_t *stream) {
    uv_write_t *req = malloc(sizeof(uv_write_t));
    req->handle = stream;
    req->bufs = malloc(sizeof(uv_buf_t));
    req->bufs[0] = uv_buf_init("Hello, world!", 13);
    uv_write(req, stream, req->bufs, 1, write_cb);
}

static void connect_cb(uv_connect_t *req, int status) {
    if (status < 0) {
        fprintf(stderr, "Connect error: %s\n", uv_strerror(status));
        return;
    }

    fprintf(stdout, "Connected to %s:%d\n", DEFAULT_HOST, DEFAULT_PORT);

    write_data(req->handle);
}

int main() {
    uv_loop_t *loop = uv_default_loop();
    uv_tcp_t client;
    uv_tcp_init(loop, &client);

    struct sockaddr_in addr;
    uv_ip4_addr(DEFAULT_HOST, DEFAULT_PORT, &addr);

    fprintf(stdout, "Connecting to %s:%d\n", DEFAULT_HOST, DEFAULT_PORT);
    uv_connect_t connect_req;
    uv_tcp_connect(&connect_req, &client, (const struct sockaddr *)&addr, connect_cb);

    int ret = uv_run(loop, UV_RUN_DEFAULT);

    uv_loop_close(loop);

    return ret;
}